package cn.sc.summer.security.provider.base;

import cn.sc.summer.constant.service.Support;
import cn.sc.summer.constant.util.SpringUtilX;
import cn.sc.summer.security.service.user.info.base.UserInfoService;
import cn.sc.summer.security.service.user.lock.base.BaseUserLockCheck;
import cn.sc.summer.token.base.BaseLoginToken;
import cn.sc.summer.token.po.UserDetailX;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

/**
 * 类名：自定义认证流程编排
 *
 * @author a-xin
 * @date 2024/6/12 09:47
 */
@Slf4j
public abstract class BaseAuthenticationProvider implements AuthenticationProvider, Support {

    private static final long serialVersionUID = 4603174025590705888L;

    /**
     * 统一的登录认证流程，全局登录认证使用这一套的流程来进行过滤，可以增强拓展性
     *
     * @param authentication the authentication request object.
     * @return 认证成功
     * @throws AuthenticationException 认证异常
     */
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        try {
            log.info("==> Started the login preCheck...");
            preCheckUser(authentication);
            log.info("==> Start getting login user information: {}...", authentication.getPrincipal());
            UserDetailX userDetailX = getUserDetail(authentication);
            log.info("==> Start check the user lock info: {}", userDetailX.getUsername());

            BaseUserLockCheck baseUserLockCheck = getUserLockCheck();
            baseUserLockCheck.checkUserLoginLock(userDetailX.getUserId());
            log.info("==> Verify the user's login information begins...");
            postCheckUser(userDetailX, authentication);
            safeUserDetail(userDetailX);
            baseUserLockCheck.deletedUserLoginLock(userDetailX.getUserId());

            log.info("==> The user login verification is passed...");
            afterCheckUser(userDetailX, authentication);
            return checkSuccess(userDetailX, authentication);
        } catch (Throwable e) {
            e.printStackTrace();
            throw new UsernameNotFoundException(e.getMessage());
        }
    }

    /**
     * 获取当前方法支持的用户锁校验类
     *
     * @return 用户锁校验
     */
    protected BaseUserLockCheck getUserLockCheck() throws Throwable {
        return SpringUtilX.getSupportBean(BaseUserLockCheck.class, getSupports(), Support.DEFAULT);
    }

    /**
     * 正式验证用户信息之前，进行用户数据验证，比如，是否黑名单，是否限制登录等
     *
     * @param authentication 验证信息
     * @throws Throwable 异常信息
     */
    protected abstract void preCheckUser(Authentication authentication) throws Throwable;

    /**
     * 获取用户信息
     *
     * @param authentication 登录凭证
     * @return 用户信息
     */
    protected UserDetailX getUserDetail(Authentication authentication) {
        UserInfoService userInfoServiceX = SpringUtilX.getSupportBean(UserInfoService.class, getSupports(), Support.DEFAULT);
        return userInfoServiceX.getUserInfo(authentication);
    }

    /**
     * 认证完成后将用户信息进行安全处理
     *
     * @param userDetailX 认证后的用户信息
     */
    protected void safeUserDetail(UserDetailX userDetailX) {
        userDetailX.setLoginType(getSupports());
        userDetailX.setPassword(null);
        userDetailX.setAcesskey(null);
    }

    /**
     * 进行用户信息验证
     *
     * @param userDetailX    用户信息
     * @param authentication 登录凭证
     * @throws Throwable 验证异常
     */
    protected abstract void postCheckUser(UserDetailX userDetailX, Authentication authentication) throws Throwable;

    /**
     * 用户信息验证后置执行
     *
     * @param userDetailX    用户信息
     * @param authentication 登录凭证
     * @throws Throwable 验证异常
     */
    protected abstract void afterCheckUser(UserDetailX userDetailX, Authentication authentication) throws Throwable;

    /**
     * 创建token信息
     *
     * @param userDetailX    用户信息
     * @param authentication 凭证
     * @return 返回信息
     * @throws Throwable 创建异常
     */
    protected Authentication checkSuccess(UserDetailX userDetailX, Authentication authentication) throws Throwable {
        if (authentication instanceof BaseLoginToken) {
            BaseLoginToken baseLoginToken = (BaseLoginToken) authentication;
            baseLoginToken.setUserDetailX(userDetailX);
            return baseLoginToken;
        }
        throw new AuthenticationServiceException("The account or password is incorrect！");
    }

}
